package com.web.quartz.service.impl;

import java.util.List;

import org.quartz.CronScheduleBuilder;
import org.quartz.DailyTimeIntervalScheduleBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.web.common.basic.quartz.entity.QuartzCronTask;
import com.web.common.basic.quartz.entity.QuartzIntervalTask;
import com.web.common.basic.quartz.entity.QuartzTask;
import com.web.service.handler.error.CustomException;

/**
 * 如果需要添加的定时任务，以下方法不能满足需求，
 * 可以直接使用注入的{@link org.quartz.Scheduler}的实现类
 * {@link org.quartz.impl.StdScheduler}
 * 自定义job和trigger。
 * 
 * @author zhouhui
 * @since 1.0.0
 */
@Service
public class QuartzOperateServiceImpl {
	
	@Autowired
	private Scheduler stdScheduler;
	
	/**
	 * 自定义定时任务，判断定时任务是否存在，如果存在刷新之前的定时任务，如果不存在则创建定时任务
	 * @param job 任务信息
	 * @param trigger 任务触发时机
	 * @return boolean true创建成功；false创建失败
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	public boolean customQuartzJob(JobDetail job, Trigger trigger) {
		try {
			if(stdScheduler.checkExists(job.getKey())) {
				stdScheduler.rescheduleJob(trigger.getKey(), trigger);
			}else {
				stdScheduler.scheduleJob(job, trigger);
			}
		} catch (SchedulerException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	

	/**
	 * 根据定时任务的名称删除定时任务
	 * @param jobName 定时任务名称
	 * @return boolean true删除成功；false删除失败
	 *
	 * @author zhouhui
	 * @since 1.0.0 
	 */
	public boolean deleteQuartzJob(String jobName, String jobGroup) {
		if(!StringUtils.hasText(jobName)) {
			throw new CustomException("未填写需要删除的任务名称");
		}
		JobKey jobKey = null;
		if(StringUtils.hasText(jobGroup)) {
			jobKey = JobKey.jobKey(jobName, jobGroup);
		}else {
			jobKey = JobKey.jobKey(jobName);
		}
		try {
			return stdScheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 执行一次定时任务
	 * @param jobName 定时任务名称
	 * @return boolean true删除成功；false删除失败
	 *
	 * @author zhouhui
	 * @since 1.0.0 
	 */
	public boolean execJobOneTimes(String jobName, String jobGroup) {
		try {
			//判断一下定时任务是否存在
			JobKey jobKey = null;
			if(StringUtils.hasText(jobGroup)) {
				jobKey = JobKey.jobKey(jobName, jobGroup);
			}else {
				jobKey = JobKey.jobKey(jobName);
			}
			if(stdScheduler.checkExists(jobKey)) {
				stdScheduler.triggerJob(jobKey);
			}else {
				throw new CustomException("定时任务不存在");
			}
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
		return true;
	}
	
	/**
	 * 简单通过cron表达式进行定时任务的执行。
	 * @param task 任务信息
	 * @return boolean true创建成功；false创建失败
	 *
	 * @author zhouhui
	 * @since 1.0.0 
	 */
	public boolean cronSimpleQuartz(QuartzCronTask task) {
		taskParamVerify(task);
		if(!StringUtils.hasText(task.getCron())) {
			throw new CustomException("未填写cron表达式");
		}
		
		//任务信息
		JobDetail job = createJobDetail(task);
		
		//创建任务触发时机
		CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(task.getCron()).withMisfireHandlingInstructionDoNothing();
		
		Trigger trigger = createTrigger(job, task, cron);
		
		return customQuartzJob(job, trigger);
	}
	
	/**
	 * 固定循环执行定时任务。<br>
	 * 注：时间间隔单位秒，可以设置固定执行次数。<br>
	 * @param task 任务信息
	 * @return boolean true创建成功；false创建失败。
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	public boolean intervalSimpleQuartz(QuartzIntervalTask task) {
		taskParamVerify(task);
		if(task.getInterval() == null) {
			throw new CustomException("未设置执行的间隔时间");
		}
		//任务信息
		JobDetail job = createJobDetail(task);
		
		//创建任务的触发时机
		DailyTimeIntervalScheduleBuilder interval = DailyTimeIntervalScheduleBuilder
				.dailyTimeIntervalSchedule()
				.withInterval(task.getInterval(), IntervalUnit.SECOND);
		if(task.getRepeatCount() <= 0) {
			//如果重复次数等于0（不进行重复会自行），则超过时间立即进行执行
			interval.withMisfireHandlingInstructionFireAndProceed();
		}else {
			//如果重复次数大于1次，则超过时间不进行执行
			interval.withMisfireHandlingInstructionDoNothing();
		}
		if(task.getRepeatCount() != null) {
			interval.withRepeatCount(task.getRepeatCount());
		}
		Trigger trigger = createTrigger(job, task, interval);
		
		return customQuartzJob(job, trigger);
	}
	
	/**
	 * 定时任务的暂停或者启动
	 * @param task 任务信息
	 * @return boolean true暂停/启动成功；false暂停/启动失败
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	public boolean quartzPauseResume(String jobName, String jobGroup) {
		try {
			List<? extends Trigger> triggerList = stdScheduler.getTriggersOfJob(JobKey.jobKey(jobName));
			if(triggerList == null || triggerList.isEmpty()) {
				return false;
			}
			JobKey jobKey = null;
			if(StringUtils.hasText(jobGroup)) {
				jobKey = JobKey.jobKey(jobName, jobGroup);
			}else {
				jobKey = JobKey.jobKey(jobName);
			}
			for(Trigger item: triggerList) {
				if(stdScheduler.getTriggerState(item.getKey()).equals(Trigger.TriggerState.PAUSED)) {
					stdScheduler.resumeJob(jobKey);
				}else {
					stdScheduler.pauseJob(jobKey);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return true;
	}
	
	/**
	 * 创建定时任务信息
	 * @param task 定时任务信息
	 * @return JobDetail 定时任务
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	private JobDetail createJobDetail(QuartzTask task) {
		//任务信息
		JobBuilder jobBuilder = JobBuilder
				.newJob(task.getJobClass());
		if(StringUtils.hasText(task.getGroupName())) {
			jobBuilder.withIdentity(task.getJobName(), task.getGroupName());
		}else {
			jobBuilder.withIdentity(task.getJobName());
		}
		if (task.getParam() != null && !task.getParam().isEmpty()) {
			JobDataMap jobDataMap = new JobDataMap(task.getParam());
			jobBuilder.usingJobData(jobDataMap);
		}
		if(StringUtils.hasText(task.getDescription())) {
			jobBuilder.withDescription(task.getDescription());
		}
		return jobBuilder.build();
	}
	
	/**
	 * 创建定时任务触发器
	 * @param job 具体任务信息
	 * @param task 定时任务信息
	 * @param scheduleBuilder 定时任务执行器
	 * @return Trigger 生成定时任务触发器
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	private Trigger createTrigger(JobDetail job, QuartzTask task, ScheduleBuilder<?> scheduleBuilder) {
		TriggerBuilder<?> triggerBuilder = TriggerBuilder
				.newTrigger()
				.forJob(job)
				.withIdentity(task.getTriggerName())
				.withSchedule(scheduleBuilder);
		if (task.getStartAt() != null) {
			triggerBuilder.startAt(task.getStartAt());
		}
		if (task.getEndAt() != null) {
			triggerBuilder.endAt(task.getEndAt());
		}
		return triggerBuilder.build();
	}
	
	/**
	 * 创建定时任务参数校验
	 * @param task 任务信息
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	private void taskParamVerify(QuartzTask task) {
		if(!StringUtils.hasText(task.getJobName())) {
			throw new CustomException("未填写任务名称");
		}
		
		//获取QuartzJobBean的类
		getJobClass(task);
		if(task.getJobClass() == null) {
			throw new CustomException("未填写任务执行方法");
		}
		
		if(!StringUtils.hasText(task.getTriggerName())) {
			task.setTriggerName(task.getJobName());
		}
	}
	
	/**
	 * 根据具体的类获取QuartzJobBean
	 * @param task 任务信息
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	@SuppressWarnings("unchecked")
	private void getJobClass(QuartzTask task) {
		if(task.getJobClass() == null) {
			//根据具体的类获取QuartzJobBean
			Class<?> cls = null;
			try {
				cls = Class.forName(task.getJobClassName());
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			if(cls != null && QuartzJobBean.class.isAssignableFrom(cls)) {
				task.setJobClass((Class<? extends QuartzJobBean>) cls);
			}
		}
	}
}
